#pragma once

#include "fd_chan.h"
#include "qconf.h"
#include "tcp_conn.h"

namespace qio {

class TcpAcceptor final {
 public:
  using OnClosedCallback = std::function<void(TcpAcceptor *)>;
  using OnErrorCallback = std::function<void(TcpAcceptor *, int, const char *)>;
  using ManagedConnMap = std::map<std::uint64_t, SharedTcpConn>;
  using TimeoutHandleGenerator =
      std::function<SharedTimeoutHandle(const SharedTcpConn &)>;
  using UniqueBufferGenerator = std::function<UniqueBuffer()>;

  struct Callbacks final {
    friend class TcpAcceptor;

    OnClosedCallback close_cb{nullptr};
    OnErrorCallback error_cb{nullptr};

   private:
    void Reset() QIO_NOEXCEPT {
      close_cb = nullptr;
      error_cb = nullptr;
    }

    void OnClosed(TcpAcceptor *acceptor) const QIO_NOEXCEPT {
      if (close_cb) {
        close_cb(acceptor);
      }
    }

    void OnError(TcpAcceptor *acceptor, int err_code,
                 const char *err_msg) const QIO_NOEXCEPT {
      if (error_cb) {
        error_cb(acceptor, err_code, err_msg);
      }
    }
  };

  struct Config {
    friend class TcpAcceptor;

    Fd fd{-1};                                       // required
    Callbacks acceptor_callbacks{};                  // optional
    TcpConn::Callbacks conn_callbacks{};             // optional
    std::shared_ptr<SSLHelper> ssl_helper{nullptr};  // optional
    char *io_buffer{nullptr};                        // readonly
    std::size_t io_buffer_len{0};                    // readonly
    TimeoutHandleGenerator timeout_generator{nullptr};
    UniqueBufferGenerator buffer_generator{nullptr};

   private:
    inline SharedTimeoutHandle GenerateTimeoutHandle(
        const SharedTcpConn &conn) const QIO_NOEXCEPT {
      return timeout_generator != nullptr ? timeout_generator(conn) : nullptr;
    }

    inline UniqueBuffer GenerateBuffer() const QIO_NOEXCEPT {
      return buffer_generator != nullptr ? buffer_generator() : nullptr;
    }
  };

  using UniqueConfig = std::unique_ptr<Config>;

 public:
  ~TcpAcceptor() QIO_NOEXCEPT;

  TcpAcceptor(EventLoop *loop, UniqueConfig &&conf) QIO_NOEXCEPT;

  void Start() QIO_NOEXCEPT;

  void Close() QIO_NOEXCEPT;

  DISABLE_CPY_MOV(TcpAcceptor)

 private:
  void InjectConnDisconnectProxy() QIO_NOEXCEPT;

  void ConfigAcceptChan() QIO_NOEXCEPT;

  void HandleAcceptEvents(FdChan *, Fd fd, int events) QIO_NOEXCEPT;

  void HandleFdClosed(FdChan *) QIO_NOEXCEPT;

  void HandleFdError(FdChan *, int err_code, const char *err_msg) QIO_NOEXCEPT;

  void ConnDisconnectedProxy(TcpConn *, const TcpConn::OnDisconnectedCallback &)
      QIO_NOEXCEPT;

  ConnId GetNextConnId() QIO_NOEXCEPT;

 private:
  EventLoop *loop_;
  UniqueConfig acceptor_conf_;
  FdChan *fd_chan_;
  std::uint64_t next_conn_id_;
  ManagedConnMap conn_map_;
};

}  // namespace qio
